/*
 * pi3's Linux kernel Runtime Guard
 *
 * Component:
 *  - Intercept *call_usermodehelper* function
 *
 * Notes:
 *  - We are maintianing Red-Black tree of pid's for Exploit Detection feature.
 *    When kernel calls user-mode helper, we need to update RB tree.
 *
 * Caveats:
 *  - None
 *
 * Timeline:
 *  - Created: 12.II.2018
 *
 * Author:
 *  - Adam 'pi3' Zabrocki (http://pi3.com.pl)
 *
 */

#include "../../../../p_lkrg_main.h"

char p_call_usermodehelper_kretprobe_state = 0;

static struct kretprobe p_call_usermodehelper_kretprobe = {
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,3,0)
    .kp.symbol_name = "____call_usermodehelper",
#else
    .kp.symbol_name = "call_usermodehelper_exec_async",
#endif
    .handler = p_call_usermodehelper_ret,
    .entry_handler = p_call_usermodehelper_entry,
    .data_size = sizeof(struct p_call_usermodehelper_data),
    /* Probe up to 40 instances concurrently. */
    .maxactive = 40,
};

static const char * const p_umh_global[] = {
   UMH_UNIFIFW
   UMH_LNET_UPCALL
   UMH_OSD_LOGIN
   "/bin/false",
   "/bin/true",
   "/etc/acpi/events/RadioPower.sh",
   "/etc/acpi/wireless-rtl-ac-dc-power.sh",
   "/lib/systemd/systemd-cgroups-agent",
   "/sbin/bridge-stp",
   "/sbin/critical_overtemp",
   "/sbin/drbdadm",
   "/sbin/hotplug",
   "/sbin/modprobe",
   "/sbin/nfs_cache_getent",
   "/sbin/nfsd-recall-failed",
   "/sbin/nfsdcltrack",
   "/sbin/ocfs2_hb_ctl",
   "/sbin/pnpbios",
   "/sbin/poweroff",
   "/sbin/request-key",
   "/sbin/tomoyo-init",
   "/sbin/v86d",
   "/system/bin/start",
   "/usr/lib/systemd/systemd-cgroups-agent",
   "/usr/lib/systemd/systemd-coredump",
   "/usr/sbin/eppfpga",
   "/usr/share/apport/apport",
};

int p_call_usermodehelper_entry(struct kretprobe_instance *p_ri, struct pt_regs *p_regs) {

   struct p_ed_process *p_tmp;
   struct subprocess_info *p_subproc = (struct subprocess_info *)p_regs_get_arg1(p_regs);
   unsigned char p_umh_allowed = 0;
   unsigned long p_flags;
   size_t i;

   p_ed_enforce_validation();

   if (!P_CTRL(p_umh_validate)) {
      goto p_call_usermodehelper_entry_out;
   } else if (P_CTRL(p_umh_validate) == 2) {
      goto p_call_usermodehelper_entry_not_allowed;
   }

   p_tasks_write_lock(&p_flags);
   if ( (p_tmp = p_find_ed_by_pid(task_pid_nr(current))) != NULL) {
      // This process is on the ED list - set temporary 'disable' flag!
      p_set_ed_process_off(p_tmp);
   }
   p_tasks_write_unlock(&p_flags);

   for (i = 0; i < nitems(p_umh_global); i++) {
      if (!strcmp(p_umh_global[i], p_subproc->path)) {
         p_umh_allowed = 1;
         break;
      }
   }

#if defined(CONFIG_STATIC_USERMODEHELPER)
   if (!strcmp(CONFIG_STATIC_USERMODEHELPER_PATH,p_subproc->path)) {
      p_umh_allowed = 1;
   }
#endif

   if (!p_umh_allowed) {
p_call_usermodehelper_entry_not_allowed:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,18,0) && LINUX_VERSION_CODE < KERNEL_VERSION(5,9,0)
      if (!strcmp("none",p_subproc->path) && p_subproc->file) {
         p_print_log(P_LKRG_ERR,
                "<Exploit Detection> UMH is executing file from memory...\n");
      } else {
#endif
         switch (P_CTRL(p_umh_enforce)) {

            /* Panic */
            case 2:
              panic(P_LKRG_SIGNATURE
                     "Blocked usermodehelper execution of [%s]\n",
                     p_subproc->path);
              break;

            /* Prevent execution */
            case 1:
              p_print_log(P_LKRG_CRIT,
                     "Blocked usermodehelper execution of [%s]\n",
                     p_subproc->path);
              p_force_sig(SIGKILL);
              break;

            /* Log only */
            case 0:
              p_print_log(P_LKRG_CRIT,
                     "Detected usermodehelper execution of [%s]\n",
                     p_subproc->path);
              break;

         }
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,18,0) && LINUX_VERSION_CODE < KERNEL_VERSION(5,9,0)
      }
#endif
   }

p_call_usermodehelper_entry_out:

   return 0;
}


int p_call_usermodehelper_ret(struct kretprobe_instance *ri, struct pt_regs *p_regs) {

   p_ed_enforce_validation();

   return 0;
}


GENERATE_INSTALL_FUNC(call_usermodehelper)
